﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sanford.Multimedia;
using Sanford.Multimedia.Midi;
using BurningSphere.Simulator.Properties;
using System.Windows.Forms;

namespace BurningSphere.Simulator.MIDI
{
	/// <summary>
	/// Midi Listener
	/// </summary>
	public class MidiListener : IDisposable
	{
		const int SysExBufferSize = 128;
		InputDevice inputDevice = null;
		byte[] lastNote = new byte[16];
		byte[,] controllers = new byte[16, 128];

		public int NumChanMsgs = 0;

		/// <summary>
		/// Init
		/// </summary>
		public void Init()
		{
			if (InputDevice.DeviceCount == 0)
			{
				//no devices
				Close();
			}
			else
			{
				int d = InputDevice.DeviceCount;
				inputDevice = new InputDevice(Settings.Default.MidiDevice);
				inputDevice.ChannelMessageReceived += HandleChannelMessageReceived;
				inputDevice.SysCommonMessageReceived += HandleSysCommonMessageReceived;
				inputDevice.SysExMessageReceived += HandleSysExMessageReceived;
				inputDevice.SysRealtimeMessageReceived += HandleSysRealtimeMessageReceived;
				inputDevice.Error += new EventHandler<ErrorEventArgs>(OnInputDeviceError);
			}

		}

		/// <summary>
		/// Device accessor
		/// </summary>
		public InputDevice Device
		{
			get
			{
				return inputDevice;
			}
		}

		/// <summary>
		/// Close
		/// </summary>
		public void Close()
		{
			if (inputDevice != null)
			{
				inputDevice.Close();
			}
		}

		/// <summary>
		/// Start
		/// </summary>
		public void Start()
		{
			inputDevice.StartRecording();
		}

		/// <summary>
		/// Stop
		/// </summary>
		public void Stop()
		{
			inputDevice.StopRecording();
			inputDevice.Reset();
		}

		/// <summary>
		/// On Device Error
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void OnInputDeviceError(object sender, ErrorEventArgs e)
		{
			MessageBox.Show(e.Error.Message, "Error!", MessageBoxButtons.OK, MessageBoxIcon.Stop);
		}

		/// <summary>
		/// Handle channel messages
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void HandleChannelMessageReceived(object sender, ChannelMessageEventArgs e)
		{
			int channel = e.Message.MidiChannel;
			switch (e.Message.Command)
			{
				case ChannelCommand.NoteOff:
					lastNote[channel] = 0;
					break;
				case ChannelCommand.NoteOn:
					lastNote[channel] = (byte)e.Message.Data1;    //ignore velocity
					break;
				case ChannelCommand.Controller:
					controllers[channel, e.Message.Data1] = (byte)e.Message.Data2;
					break;

			}
			NumChanMsgs++;
			/*
			context.Post(delegate(object dummy)
			{
				channelListBox.Items.Add(
					e.Message.Command.ToString() + '\t' + '\t' +
					e.Message.MidiChannel.ToString() + '\t' +
					e.Message.Data1.ToString() + '\t' +
					e.Message.Data2.ToString());

				channelListBox.SelectedIndex = channelListBox.Items.Count - 1;
			}, null);
			 */

		}

		/// <summary>
		/// HandleSysExMessageReceived
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void HandleSysExMessageReceived(object sender, SysExMessageEventArgs e)
		{
			NumChanMsgs++;
			/*
			context.Post(delegate(object dummy)
			{
				string result = "\n\n"; ;

				foreach(byte b in e.Message)
				{
					result += string.Format("{0:X2} ", b);
				}

				sysExRichTextBox.Text += result;
			}, null);
			 */
		}

		/// <summary>
		/// HandleSysCommonMessageReceived
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void HandleSysCommonMessageReceived(object sender, SysCommonMessageEventArgs e)
		{
			NumChanMsgs++;
			/*
			context.Post(delegate(object dummy)
			{
				sysCommonListBox.Items.Add(
					e.Message.SysCommonType.ToString() + '\t' + '\t' +
					e.Message.Data1.ToString() + '\t' +
					e.Message.Data2.ToString());

				sysCommonListBox.SelectedIndex = sysCommonListBox.Items.Count - 1;
			}, null);
			 * */
		}

		/// <summary>
		/// HandleSysRealtimeMessageReceived
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void HandleSysRealtimeMessageReceived(object sender, SysRealtimeMessageEventArgs e)
		{
			NumChanMsgs++;
			/*
			context.Post(delegate(object dummy)
			{
				sysRealtimeListBox.Items.Add(
					e.Message.SysRealtimeType.ToString());

				sysRealtimeListBox.SelectedIndex = sysRealtimeListBox.Items.Count - 1;
			}, null);
			 */
		}

		#region IDisposable Members

		public void Dispose()
		{
			if (inputDevice != null)
			{
				inputDevice.Dispose();
				inputDevice = null;
			}
		}

		#endregion
	}
}